﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ReLogic.Content;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using Terraria;
using Terraria.DataStructures;
using Terraria.Graphics.Shaders;
using Terraria.ID;
using Terraria.Utilities;

namespace HardcoreGame.Common.Helpers
{
    public static partial class MyUtils
    {
        public const int RealLifeSecond = 60;
        private static readonly List<DelayedAction> _delayedActions = [];
       
        public static Vector2 GetRandomPoint(this Rectangle rect)
        {
            return new Vector2((float)rect.X + (float)rect.Width * (float)Main.rand.NextDouble(), (float)rect.Y + (float)rect.Height * (float)Main.rand.NextDouble());
        }       
        
        public static void Move(this Projectile projectile, Vector2 vector, float speed, float turnResistance = 10f, bool toPlayer = false)
        {
            Player player = Main.player[projectile.owner];
            Vector2 move = (toPlayer ? player.Center + vector : vector) - projectile.Center;
            float magnitude = Magnitude(move);
            if (magnitude > speed)
            {
                move *= speed / magnitude;
            }
            move = (projectile.velocity * turnResistance + move) / (turnResistance + 1f);
            magnitude = Magnitude(move);
            if (magnitude > speed)
            {
                move *= speed / magnitude;
            }
            projectile.velocity = move;
        }
        public static Vector2 ScreenCenter
        {
            get
            {
                return new Vector2(Main.screenPosition.X + Main.screenWidth * 0.5f, Main.screenPosition.Y + Main.screenHeight * 0.5f);
            }
            set
            {
                Main.screenPosition = new Vector2(value.X - Main.screenWidth * 0.5f, value.Y - Main.screenHeight * 0.5f);
            }
        }
        public static float StepTowards(float val, float goal, float step)
        {
            if (goal > val)
            {
                val += step;
                if (val > goal)
                {
                    val = goal;
                }
            }
            else if (goal < val)
            {
                val -= step;
                if (val < goal)
                {
                    val = goal;
                }
            }
            return val;
        }
        public static float Magnitude(Vector2 mag)
        {
            return (float)Math.Sqrt((double)(mag.X * mag.X + mag.Y * mag.Y));
        }
        public static Color MultiplyAlpha(this Color c, float alpha)
        {
            return new Color(c.R, c.G, c.B, (int)(c.A / 255f * MathHelper.Clamp(alpha, 0f, 1f) * 255f));
        }
        public static float Map(int value, int inMin, int inMax, float outMin, float outMax)
        {
            float output = (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
            return Math.Clamp(output, outMin, outMax);
        }
        public static float NextFloatRange(this UnifiedRandom rng, float range)
        {
            return rng.NextFloat(-range, range);
        }
        public static float QuadraticEase(float x, float xMin, float xMax, float yMin, float yMax, bool isConcave, float peakPercentage = 0.5f)
        {
            peakPercentage = Math.Clamp(peakPercentage, 0f, 1f);
            float xPeak = xMin + (xMax - xMin) * peakPercentage;
            x = Math.Clamp(x, xMin, xMax);
            float yRange = yMax - yMin;
            bool flag = x <= xPeak;
            float t;
            if (flag)
            {
                t = (x - xPeak) / (xMin - xPeak);
            }
            else
            {
                t = (x - xPeak) / (xMax - xPeak);
            }
            float num;
            if (isConcave)
            {
                num = yMin + yRange * (1f - t * t);
            }
            else
            {
                num = yMax - yRange * (1f - t * t);
            }
            return num;
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void BeginBlendState(this SpriteBatch spriteBatch, BlendState state, SamplerState samplerState = null, RasterizerState rasterizerState = null, bool dontEnd = false, bool ui = false, DrawMode mode = DrawMode.World, bool immediate = false)
        {
            SpriteSortMode sort = immediate ? SpriteSortMode.Immediate : SpriteSortMode.Deferred;
            Matrix matrix = Main.GameViewMatrix.TransformationMatrix;
            if (ui)
            {
                mode = DrawMode.UI;
            }
            if (mode != DrawMode.UI)
            {
                if (mode == DrawMode.Tile)
                {
                    matrix = Matrix.Identity;
                }
            }
            else
            {
                sort = SpriteSortMode.Deferred;
                matrix = Main.UIScaleMatrix;
            }
            if (!dontEnd)
            {
                spriteBatch.End();
            }
            spriteBatch.Begin(sort, state, samplerState ?? Main.DefaultSamplerState, DepthStencilState.None, rasterizerState ?? RasterizerState.CullCounterClockwise, null, matrix);
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void EndBlendState(this SpriteBatch spriteBatch, bool ui = false, DrawMode mode = DrawMode.World, bool immediate = false)
        {
            SpriteSortMode sort = SpriteSortMode.Deferred;
            Matrix matrix = Main.GameViewMatrix.TransformationMatrix;
            SamplerState sampler = Main.DefaultSamplerState;
            if (ui)
            {
                mode = DrawMode.UI;
            }
            if (mode != DrawMode.UI)
            {
                if (mode == DrawMode.Tile)
                {
                    matrix = Matrix.Identity;
                }
            }
            else
            {
                sort = SpriteSortMode.Deferred;
                sampler = SamplerState.AnisotropicClamp;
                matrix = Main.UIScaleMatrix;
            }
            spriteBatch.End();
            spriteBatch.Begin(immediate ? SpriteSortMode.Immediate : sort, BlendState.AlphaBlend, sampler, DepthStencilState.None, RasterizerState.CullCounterClockwise, null, matrix);
        }
        public static void Delay(int delayFrames, Action callback)
        {
            _delayedActions.Add(new DelayedAction(delayFrames, callback));
        }
        public static void UpdateTimers()
        {
            for (int i = _delayedActions.Count - 1; i >= 0; i--)
            {
                if (_delayedActions[i].IsComplete)
                {
                    _delayedActions.RemoveAt(i);
                }
                else
                {
                    _delayedActions[i].Update();
                }
            }
        }
        public static bool IsItemExposedToRain(Item item)
        {
            Vector2 center = item.Center;
            float detectionHeight = 160f;
            int startX = (int)(center.X / 16);
            int startY = (int)(center.Y / 16);
            int endY = (int)((center.Y - detectionHeight) / 16);
            if (endY < 0 || startY >= Main.maxTilesY || startX < 0 || startX >= Main.maxTilesX)
                return false;
            for (int y = startY; y >= endY; y--)
            {
                Tile tile = Main.tile[startX, y];

                if (tile.HasTile && (Main.tileSolid[tile.TileType] || Main.tileSolidTop[tile.TileType]))
                {
                    return false;
                }
            }

            return true;
        }
        public static void SimpleDrawProjectile(this Projectile Projectile, Texture2D texture, Color Color, bool IsGlow, float scaleMod = 1f, float extraRot = 0f)
        {
            SpriteEffects spriteEffects = (SpriteEffects)(Projectile.spriteDirection == -1 ? 1 : 0);
            int frameHeight = texture.Height / Main.projFrames[Projectile.type];
            int startY = frameHeight * Projectile.frame;
            Rectangle sourceRectangle = new(0, startY, texture.Width, frameHeight);
            Vector2 origin = sourceRectangle.Size() / 2f;
            Vector2 drawPos = Projectile.Center - Main.screenPosition;
            Main.EntitySpriteDraw(texture, drawPos, new Rectangle?(sourceRectangle), IsGlow ? Color : Projectile.GetAlpha(Color), Projectile.rotation + extraRot, origin, Projectile.scale * scaleMod, spriteEffects, 0f);
        }
        public static Dust CreateDust(int type, Vector2 vel, Vector2 pos, Color col, float scale = 1f, int alpha = 0, bool rotate = false, bool noGrav = true)
        {
            Dust dust = Dust.NewDustPerfect(pos, type, null, 0, default, 1f);
            dust.position = pos;
            dust.velocity = !rotate ? vel : vel.RotatedByRandom(6.2831854820251465);
            dust.color = col;
            dust.alpha = alpha;
            dust.scale = scale;
            dust.noGravity = noGrav;
            return dust;
        }
        public static void BasicInWorldGlowmask(Item item, SpriteBatch spriteBatch, Texture2D glowTexture, Color color, float rotation, float scale)
        {
            spriteBatch.Draw(glowTexture, new Vector2(item.position.X - Main.screenPosition.X + item.width * 0.5f, item.position.Y - Main.screenPosition.Y + item.height - glowTexture.Height * 0.5f), new Rectangle?(new Rectangle(0, 0, glowTexture.Width, glowTexture.Height)), color, rotation, glowTexture.Size() * 0.5f, scale, 0, 0f);
        }
        public static void PushBlendState(this SpriteBatch spriteBatch, BlendState state, Action drawAction, SamplerState samplerState = null)
        {
            spriteBatch.PushBlendState(state, false, drawAction, samplerState);
        }
        public static void PushBlendState(this SpriteBatch spriteBatch, BlendState state, bool isUI, Action drawAction, SamplerState samplerState = null)
        {
            spriteBatch.End();
            spriteBatch.Begin(SpriteSortMode.Immediate, state, samplerState ?? Main.DefaultSamplerState, DepthStencilState.None, RasterizerState.CullCounterClockwise, null, isUI ? Main.UIScaleMatrix : Main.GameViewMatrix.TransformationMatrix);
            drawAction();
            spriteBatch.End();
            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, isUI ? SamplerState.AnisotropicClamp : Main.DefaultSamplerState, DepthStencilState.None, RasterizerState.CullCounterClockwise, null, isUI ? Main.UIScaleMatrix : Main.GameViewMatrix.TransformationMatrix);
        }
        public static void CleanHoldStyle(Player player, float desiredRotation, Vector2 desiredPosition, Vector2 spriteSize, Vector2? rotationOriginFromCenter = null, bool noSandstorm = false, bool flipAngle = false, bool stepDisplace = true)
        {
            if (noSandstorm)
                player.sandStorm = false;
            if (rotationOriginFromCenter == null)
                rotationOriginFromCenter = Vector2.Zero;
            Vector2 origin = rotationOriginFromCenter.Value;
            origin.X *= player.direction;
            origin.Y *= player.gravDir;
            player.itemRotation = desiredRotation;
            if (flipAngle)
                player.itemRotation *= player.direction;
            else if (player.direction < 0)
                player.itemRotation += MathHelper.Pi;
            Vector2 consistentCenterAnchor = player.itemRotation.ToRotationVector2() * -(spriteSize.X * 0.5f + 10f) * player.direction;
            Vector2 consistentAnchor = consistentCenterAnchor - origin.RotatedBy(player.itemRotation);
            Vector2 finalPosition = desiredPosition + consistentAnchor;
            if (player.HeldItem.useStyle == ItemUseStyleID.Shoot)
                finalPosition.Y -= spriteSize.Y * 0.5f;
            if (stepDisplace)
            {
                int frame = player.bodyFrame.Y / player.bodyFrame.Height;
                if (frame > 6 && frame < 10 || frame > 13 && frame < 17)
                {
                    finalPosition -= Vector2.UnitY * 2f;
                }
            }
            player.itemLocation = finalPosition;
        }
        internal static readonly FieldInfo UImageFieldMisc0 = typeof(MiscShaderData).GetField("_uImage0", BindingFlags.NonPublic | BindingFlags.Instance);
        internal static readonly FieldInfo UImageFieldMisc1 = typeof(MiscShaderData).GetField("_uImage1", BindingFlags.NonPublic | BindingFlags.Instance);
        public static MiscShaderData SetShaderTexture(this MiscShaderData shader, Asset<Texture2D> texture, int index = 1)
        {
            switch (index)
            {
                case 0:
                    UImageFieldMisc0.SetValue(shader, texture);
                    break;
                case 1:
                    UImageFieldMisc1.SetValue(shader, texture);
                    break;
            }
            return shader;
        }
    }
}
